# Podemos validarlo contando la cantidad de valores duplicados
sum(raw_data$InicioVisitaPlanificado == raw_data$FinVisitaPlanificado)[1] 27484

Uno de los primeros problemas que notamos es que la columna InicioVisitaPlanificado y FinVisitaPlanificado contienen los mismos valores.
# Podemos validarlo contando la cantidad de valores duplicados
sum(raw_data$InicioVisitaPlanificado == raw_data$FinVisitaPlanificado)[1] 27484
# Comparamos la cantidad de duplicados (27484) con la cantidad de filas (27484).
dim(raw_data)[1] 27484 16
Luego de verificar podemos unificar estas columnas en una nueva columna más intuitiva. “visita_planificada”
raw_data <- raw_data %>%
# Nueva columna para almacenar el horario planificado
mutate(visita_planificada = InicioVisitaPlanificado) %>%
# Eliminamos InicioVisitaPlanificado y FinVisitaPlanificado
select(-InicioVisitaPlanificado, -FinVisitaPlanificado)Nos aseguramos que todas las columnas tengan el formato correcto.
# Nombre de las columnas actuales
colnames(raw_data) [1] "iddomicilioorden" "direccion" "localidad"
[4] "InicioHorario1" "FinHorario1" "latitud"
[7] "longitud" "cliente" "mes"
[10] "Bultos" "Peso" "Unidades"
[13] "InicioVisitaReal" "FinVisitaReal" "visita_planificada"
# Clases de cada columna
sapply(raw_data, class)$iddomicilioorden
[1] "numeric"
$direccion
[1] "character"
$localidad
[1] "character"
$InicioHorario1
[1] "numeric"
$FinHorario1
[1] "numeric"
$latitud
[1] "numeric"
$longitud
[1] "numeric"
$cliente
[1] "numeric"
$mes
[1] "numeric"
$Bultos
[1] "numeric"
$Peso
[1] "numeric"
$Unidades
[1] "numeric"
$InicioVisitaReal
[1] "character"
$FinVisitaReal
[1] "character"
$visita_planificada
[1] "POSIXct" "POSIXt"
Tenemos que pasar a fecha las columnas: InicioVisitaReal, FinVisitaReal. Tambien nos aseguramos de que VisitaPlanificado tenga el mismo formato.
# Convertir cada columna a formato de fecha y hora
raw_data$InicioVisitaReal <- as.POSIXct(raw_data$InicioVisitaReal, format="%Y-%m-%d %H:%M:%OS")
raw_data$FinVisitaReal <- as.POSIXct(raw_data$FinVisitaReal, format="%Y-%m-%d %H:%M:%OS")
raw_data$visita_planificada <- as.POSIXct(raw_data$visita_planificada, format="%Y-%m-%d %H:%M:%OS")Las columnas InicioHorario1, FinHorario1, las pasamos a caracter para categorizarlas más facil.
# Convertir las columnas a carácter
raw_data$InicioHorario1 <- as.character(raw_data$InicioHorario1)
raw_data$FinHorario1 <- as.character(raw_data$FinHorario1)raw_data$cliente <- as.factor(raw_data$cliente)Para facilitar el uso del dataset renombramos las columnas con nombres más simples y descriptivos.
colnames(raw_data) [1] "iddomicilioorden" "direccion" "localidad"
[4] "InicioHorario1" "FinHorario1" "latitud"
[7] "longitud" "cliente" "mes"
[10] "Bultos" "Peso" "Unidades"
[13] "InicioVisitaReal" "FinVisitaReal" "visita_planificada"
# Renombrar columnas específicas con dplyr
raw_data <- raw_data %>%
rename( id_orden = iddomicilioorden,
inicio_horario = InicioHorario1,
fin_horario = FinHorario1,
bultos = Bultos,
peso = Peso,
unidades = Unidades,
inicio_visita = InicioVisitaReal,
fin_visita = FinVisitaReal)colnames(raw_data) [1] "id_orden" "direccion" "localidad"
[4] "inicio_horario" "fin_horario" "latitud"
[7] "longitud" "cliente" "mes"
[10] "bultos" "peso" "unidades"
[13] "inicio_visita" "fin_visita" "visita_planificada"
raw_data <- raw_data %>%
select(id_orden, cliente, localidad, direccion, latitud, longitud,
bultos, unidades, peso, inicio_horario, fin_horario, visita_planificada, inicio_visita, fin_visita)Antes de crear nuevas variables vamos a asegurarnos de que los datos con los que vamos a trabajar sean correctos.
# Obtener un resumen completo del dataframe
skim(raw_data)| Name | raw_data |
| Number of rows | 27484 |
| Number of columns | 14 |
| _______________________ | |
| Column type frequency: | |
| character | 4 |
| factor | 1 |
| numeric | 6 |
| POSIXct | 3 |
| ________________________ | |
| Group variables | None |
Variable type: character
| skim_variable | n_missing | complete_rate | min | max | empty | n_unique | whitespace |
|---|---|---|---|---|---|---|---|
| localidad | 0 | 1 | 5 | 31 | 0 | 44 | 0 |
| direccion | 0 | 1 | 7 | 49 | 0 | 6072 | 0 |
| inicio_horario | 0 | 1 | 1 | 3 | 0 | 3 | 0 |
| fin_horario | 0 | 1 | 4 | 4 | 0 | 3 | 0 |
Variable type: factor
| skim_variable | n_missing | complete_rate | ordered | n_unique | top_counts |
|---|---|---|---|---|---|
| cliente | 0 | 1 | FALSE | 2 | 20: 16604, 70: 10880 |
Variable type: numeric
| skim_variable | n_missing | complete_rate | mean | sd | p0 | p25 | p50 | p75 | p100 | hist |
|---|---|---|---|---|---|---|---|---|---|---|
| id_orden | 0 | 1 | 101860.16 | 33957.94 | 74956.00 | 77468.00 | 82380.00 | 122555.00 | 183277 | ▇▁▁▁▂ |
| latitud | 41 | 1 | -34.59 | 0.30 | -34.93 | -34.62 | -34.60 | -34.58 | 0 | ▇▁▁▁▁ |
| longitud | 41 | 1 | -58.44 | 0.52 | -68.74 | -58.48 | -58.44 | -58.40 | 0 | ▇▁▁▁▁ |
| bultos | 0 | 1 | 5.76 | 12.23 | 0.10 | 2.00 | 3.00 | 6.00 | 360 | ▇▁▁▁▁ |
| unidades | 0 | 1 | 28.37 | 61.75 | 1.00 | 2.00 | 6.00 | 40.00 | 2203 | ▇▁▁▁▁ |
| peso | 0 | 1 | 41.13 | 79.07 | 0.00 | 13.00 | 20.92 | 39.00 | 2475 | ▇▁▁▁▁ |
Variable type: POSIXct
| skim_variable | n_missing | complete_rate | min | max | median | n_unique |
|---|---|---|---|---|---|---|
| visita_planificada | 0 | 1 | 2024-05-03 12:01:00 | 2024-08-06 12:01:00 | 2024-06-18 12:02:00 | 6797 |
| inicio_visita | 52 | 1 | 2024-05-03 10:17:51 | 2024-08-06 19:57:00 | 2024-06-18 13:29:31 | 16357 |
| fin_visita | 52 | 1 | 2024-05-03 11:08:53 | 2024-08-06 19:57:00 | 2024-06-18 13:42:33 | 16309 |
# Contar la cantidad de valores NA por columna
colSums(is.na(raw_data)) id_orden cliente localidad direccion
0 0 0 0
latitud longitud bultos unidades
41 41 0 0
peso inicio_horario fin_horario visita_planificada
0 0 0 0
inicio_visita fin_visita
52 52
# Verificar si hay filas duplicadas
sum(duplicated(raw_data))[1] 46
# Mostrar las filas duplicadas
raw_data[duplicated(raw_data), ] %>% arrange(desc(id_orden))# A tibble: 46 × 14
id_orden cliente localidad direccion latitud longitud bultos unidades peso
<dbl> <fct> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
1 182218 20 HUDSON CALLE 63… -34.8 -58.1 10 10 100
2 179581 20 CAPITAL FE… AEROPARQ… -34.6 -58.4 10 10 10
3 179581 20 CAPITAL FE… AEROPARQ… -34.6 -58.4 10 10 10
4 179581 20 CAPITAL FE… AEROPARQ… -34.6 -58.4 10 10 10
5 179276 20 CAPITAL FE… CARLOS P… -34.6 -58.4 4 4 26
6 179142 20 CAPITAL FE… AV CORRI… -34.6 -58.4 6 6 6
7 177940 20 CAPITAL FE… Florida … -34.6 -58.4 12 12 12
8 177939 20 CAPITAL FE… AV.PUEYR… -34.6 -58.4 12 12 12
9 177938 20 CAPITAL FE… FLORIDA … -32.9 -68.7 12 12 12
10 177937 20 CAPITAL FE… Florida … -32.9 -68.7 12 12 12
# ℹ 36 more rows
# ℹ 5 more variables: inicio_horario <chr>, fin_horario <chr>,
# visita_planificada <dttm>, inicio_visita <dttm>, fin_visita <dttm>
# Verificamos una observación duplicada
raw_data %>% filter(fin_visita == "2024-07-05 08:58:36")# A tibble: 2 × 14
id_orden cliente localidad direccion latitud longitud bultos unidades peso
<dbl> <fct> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
1 82384 20 CAPITAL FED… LAFINUR … -34.6 -58.4 9 9 45
2 82384 20 CAPITAL FED… LAFINUR … -34.6 -58.4 9 9 45
# ℹ 5 more variables: inicio_horario <chr>, fin_horario <chr>,
# visita_planificada <dttm>, inicio_visita <dttm>, fin_visita <dttm>
Mientras explorabamos las filas duplicadas encontramos que existe una gran cantidad de datos con el mismo horario y feecha de fin de visita. Y en estos casos coincide tambien con el horario de inicio.
raw_data %>% filter(fin_visita == "2024-07-03 16:55:00")# A tibble: 18 × 14
id_orden cliente localidad direccion latitud longitud bultos unidades peso
<dbl> <fct> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
1 76228 70 CAPITAL Sanchez … -34.6 -58.4 4.5 9 31.5
2 76928 70 CAPITAL Santiago… -34.6 -58.4 3.1 36 28.9
3 76997 70 SIN LOCAL… RINCON 7… -34.6 -58.4 3.6 51 23.4
4 77067 70 CAPITAL ADOLFO A… -34.6 -58.4 3.23 52 25.4
5 77123 70 CAPITAL HUMBERTO… -34.6 -58.4 5.55 81 35.4
6 77173 70 CAPITAL CATAMARC… -34.6 -58.4 6.92 94 48.2
7 79473 70 CAPITAL VENEZUEL… -34.6 -58.4 20 240 240
8 79482 70 Villa Ort… HIPOLITO… -34.6 -58.4 5.82 84 36.5
9 79490 70 CAPITAL HIPOLITO… -34.6 -58.4 2.65 53 8.40
10 79552 70 CAPITAL AV.ENTRE… -34.6 -58.4 5.7 77 42.4
11 79604 70 CAPITAL AVDA RIV… -34.6 -58.4 6.32 101 30.0
12 99060 70 CAPITAL JUJUY 457 -34.6 -58.4 3.35 65 10.3
13 100429 70 CAPITAL RIVADAVI… -34.6 -58.4 7.37 105 41.0
14 123181 70 CAPITAL PENA 2475 -34.6 -58.4 11 135 107.
15 123181 70 CAPITAL PENA 2475 -34.6 -58.4 11 135 107.
16 175069 70 CAPITAL SOLIS 757 -34.6 -58.4 3.33 56 15.1
17 177690 70 CAPITAL SANTIAGO… -34.6 -58.4 4.2 56 31.3
18 177922 70 CAPITAL MISIONES… -34.6 -58.4 7.12 103 44.8
# ℹ 5 more variables: inicio_horario <chr>, fin_horario <chr>,
# visita_planificada <dttm>, inicio_visita <dttm>, fin_visita <dttm>
summary(raw_data) id_orden cliente localidad direccion
Min. : 74956 20:16604 Length:27484 Length:27484
1st Qu.: 77468 70:10880 Class :character Class :character
Median : 82380 Mode :character Mode :character
Mean :101860
3rd Qu.:122555
Max. :183277
latitud longitud bultos unidades
Min. :-34.93 Min. :-68.74 Min. : 0.100 Min. : 1.00
1st Qu.:-34.62 1st Qu.:-58.48 1st Qu.: 2.000 1st Qu.: 2.00
Median :-34.60 Median :-58.44 Median : 3.000 Median : 6.00
Mean :-34.59 Mean :-58.44 Mean : 5.761 Mean : 28.37
3rd Qu.:-34.58 3rd Qu.:-58.40 3rd Qu.: 6.000 3rd Qu.: 40.00
Max. : 0.00 Max. : 0.00 Max. :360.000 Max. :2203.00
NA's :41 NA's :41
peso inicio_horario fin_horario
Min. : 0.00 Length:27484 Length:27484
1st Qu.: 13.00 Class :character Class :character
Median : 20.92 Mode :character Mode :character
Mean : 41.13
3rd Qu.: 39.00
Max. :2475.00
visita_planificada inicio_visita
Min. :2024-05-03 12:01:00.00 Min. :2024-05-03 07:17:51.00
1st Qu.:2024-05-24 12:59:00.00 1st Qu.:2024-05-24 15:38:00.00
Median :2024-06-18 12:02:00.00 Median :2024-06-18 10:29:31.00
Mean :2024-06-17 20:55:17.65 Mean :2024-06-17 22:30:40.35
3rd Qu.:2024-07-11 12:07:00.00 3rd Qu.:2024-07-11 11:42:07.00
Max. :2024-08-06 12:01:00.00 Max. :2024-08-06 16:57:00.00
NA's :52
fin_visita
Min. :2024-05-03 08:08:53.00
1st Qu.:2024-05-24 15:38:00.00
Median :2024-06-18 10:42:33.00
Mean :2024-06-17 22:36:39.14
3rd Qu.:2024-07-11 11:49:58.50
Max. :2024-08-06 16:57:00.00
NA's :52
El icono “🟩“ indica solucionado.
Encontramos
41 valores nulos en latitud y longitud 🟩
52 valores nulos en inicio_visita y fin_visita
46 observaciones duplicadas. 🟩
Valores de “0” en latitud y longitud. 🟩
Peso mínimo igual a 0
raw_data <- raw_data %>%
distinct()
dim(raw_data)[1] 27438 14
Para los valores nulos encontrados en cordenadas validamos si ya se encuentra algun domicilio con los datos cargados.
# Filtrar las filas donde latitud o longitud son NA
cordenadas_vacias <- raw_data %>%
filter(
is.na(latitud) | is.na(longitud) | latitud == 0 | longitud == 0
)
cordenadas_vacias # dim 43 x 14# A tibble: 42 × 14
id_orden cliente localidad direccion latitud longitud bultos unidades peso
<dbl> <fct> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
1 163957 20 CAPITAL FE… DR ENRIQ… 0 0 2 2 13
2 167030 70 CAPITAL JOSE HER… NA NA 2.44 55 10.8
3 173085 20 GENERAL PA… CTRO COM… 0 0 4 4 0
4 176714 20 CAPITAL FE… AV. R. S… NA NA 5 5 39
5 176717 20 CAPITAL FE… AV. CORR… NA NA 2 2 19.5
6 176731 70 CAPITAL VEDIA 36… NA NA 5.5 18 50.2
7 177884 20 CAPITAL FE… LARREA 1… NA NA 3 3 19.5
8 177934 20 CAPITAL FE… AV CORRI… NA NA 12 12 12
9 177988 20 CAPITAL FE… CUBA 291… NA NA 5 5 0
10 178012 70 CAPITAL AREVALO … NA NA 2 2 10
# ℹ 32 more rows
# ℹ 5 more variables: inicio_horario <chr>, fin_horario <chr>,
# visita_planificada <dttm>, inicio_visita <dttm>, fin_visita <dttm>
distinct(cordenadas_vacias)# A tibble: 42 × 14
id_orden cliente localidad direccion latitud longitud bultos unidades peso
<dbl> <fct> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
1 163957 20 CAPITAL FE… DR ENRIQ… 0 0 2 2 13
2 167030 70 CAPITAL JOSE HER… NA NA 2.44 55 10.8
3 173085 20 GENERAL PA… CTRO COM… 0 0 4 4 0
4 176714 20 CAPITAL FE… AV. R. S… NA NA 5 5 39
5 176717 20 CAPITAL FE… AV. CORR… NA NA 2 2 19.5
6 176731 70 CAPITAL VEDIA 36… NA NA 5.5 18 50.2
7 177884 20 CAPITAL FE… LARREA 1… NA NA 3 3 19.5
8 177934 20 CAPITAL FE… AV CORRI… NA NA 12 12 12
9 177988 20 CAPITAL FE… CUBA 291… NA NA 5 5 0
10 178012 70 CAPITAL AREVALO … NA NA 2 2 10
# ℹ 32 more rows
# ℹ 5 more variables: inicio_horario <chr>, fin_horario <chr>,
# visita_planificada <dttm>, inicio_visita <dttm>, fin_visita <dttm>
Revisamos para cada entrega con valores faltantes si existe alguna orden con los datos cargados correctamente.
# Filtrar las observaciones donde id_orden está en cordenadas_vacias
observaciones_id_orden <- raw_data %>%
filter(id_orden %in% cordenadas_vacias$id_orden) %>%
group_by(id_orden) %>%
summarise(count = n())
# Mostrar el resultado
observaciones_id_orden# A tibble: 39 × 2
id_orden count
<dbl> <int>
1 163957 2
2 167030 1
3 173085 9
4 176714 2
5 176717 1
6 176731 1
7 177884 3
8 177934 2
9 177988 2
10 178012 10
# ℹ 29 more rows
# Contar las apariciones de cada id_orden en cordenadas_vacias
apariciones_cordenadas_vacias <- cordenadas_vacias %>%
group_by(id_orden) %>%
summarise(na_count = n())
# Unir las tablas por id_orden
resultado <- observaciones_id_orden %>%
left_join(apariciones_cordenadas_vacias, by = "id_orden") %>%
# Si no hay coincidencias en cordenadas_vacias, establecer na_count en 0
mutate(na_count = ifelse(is.na(na_count), 0, na_count)) %>%
# Restar las apariciones de cordenadas_vacias del total
mutate(count_diff = count - na_count) %>%
# Filtrar solo los id_orden donde count_diff es mayor a 0
filter(count_diff > 0)
# Mostrar el resultado
resultado# A tibble: 21 × 4
id_orden count na_count count_diff
<dbl> <int> <int> <int>
1 163957 2 1 1
2 173085 9 1 8
3 176714 2 1 1
4 177884 3 1 2
5 177934 2 1 1
6 177988 2 1 1
7 178012 10 1 9
8 179363 2 1 1
9 179364 2 1 1
10 180498 2 1 1
# ℹ 11 more rows
Podemos reemplazar la latitud y longitud en 21 de las 42 filas. Podemos recuperar un 50% de los datos con latitud y longitud faltante. Para esto creamos una función
# Verificamos una observación
raw_data %>% filter(id_orden == 177934)# A tibble: 2 × 14
id_orden cliente localidad direccion latitud longitud bultos unidades peso
<dbl> <fct> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
1 177934 20 CAPITAL FED… AV CORRI… NA NA 12 12 12
2 177934 20 CAPITAL FED… AV CORRI… -34.6 -58.4 3 3 4
# ℹ 5 more variables: inicio_horario <chr>, fin_horario <chr>,
# visita_planificada <dttm>, inicio_visita <dttm>, fin_visita <dttm>
# Cantidad previa de NA (40)
summary(raw_data$longitud) Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
-68.74 -58.48 -58.44 -58.44 -58.40 0.00 40
# Definir la función que revisa y sobrescribe latitud y longitud
reparar_lat_long <- function(dataset, ids) {
# Iterar sobre cada id de la lista
for (id in ids) {
# Filtrar las observaciones válidas de latitud y longitud para este id_orden
observaciones_validas <- dataset %>%
filter(id_orden == id & !is.na(latitud) & !is.na(longitud) & latitud != 0 & longitud != 0)
# Si existen observaciones válidas, tomar la primera ocurrencia
if (nrow(observaciones_validas) > 0) {
latitud_valida <- observaciones_validas$latitud[1]
longitud_valida <- observaciones_validas$longitud[1]
# Sobrescribir las observaciones con latitud o longitud nulos o 0
dataset <- dataset %>%
mutate(
latitud = ifelse(id_orden == id & (is.na(latitud) | latitud == 0), latitud_valida, latitud),
longitud = ifelse(id_orden == id & (is.na(longitud) | longitud == 0), longitud_valida, longitud)
)
}
}
# Retornar el dataset reparado
return(dataset)
}
# Ejecutar la función usando los id_orden de la columna resultado
ids_a_reparar <- resultado$id_orden
# Aplicar la función a raw_data
raw_data <- reparar_lat_long(raw_data, ids_a_reparar)
# Verificar los cambios
head(raw_data)# A tibble: 6 × 14
id_orden cliente localidad direccion latitud longitud bultos unidades peso
<dbl> <fct> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
1 74956 70 CAPITAL VIDAL 2044 -34.6 -58.5 2.52 30 24.9
2 74956 70 CAPITAL VIDAL 2044 -34.6 -58.5 2.87 38 26.3
3 74956 70 CAPITAL VIDAL 2044 -34.6 -58.5 2.4 32 24.9
4 74956 70 CAPITAL VIDAL 2044 -34.6 -58.5 1.8 28 14.0
5 74956 70 CAPITAL VIDAL 2044 -34.6 -58.5 2.12 31 14.4
6 74956 70 CAPITAL VIDAL 2044 -34.6 -58.5 1.82 25 13.8
# ℹ 5 more variables: inicio_horario <chr>, fin_horario <chr>,
# visita_planificada <dttm>, inicio_visita <dttm>, fin_visita <dttm>
# Comprobamos que funciona.
raw_data %>% filter(id_orden == 177934)# A tibble: 2 × 14
id_orden cliente localidad direccion latitud longitud bultos unidades peso
<dbl> <fct> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
1 177934 20 CAPITAL FED… AV CORRI… -34.6 -58.4 12 12 12
2 177934 20 CAPITAL FED… AV CORRI… -34.6 -58.4 3 3 4
# ℹ 5 more variables: inicio_horario <chr>, fin_horario <chr>,
# visita_planificada <dttm>, inicio_visita <dttm>, fin_visita <dttm>
Con todos estos cambios comprobamos la cantidad de datos vacios en latitud / longitud.
# Nueva cantidad de NA (19), 21 solucionados.
summary(raw_data$longitud) Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
-68.74 -58.48 -58.44 -58.44 -58.40 -57.94 19
Quedaron 19 sin resolver, eliminamos estas filas.
# Eliminar las filas donde latitud o longitud es NA
raw_data <- raw_data %>%
filter(!is.na(latitud) & !is.na(longitud))
dim(raw_data)[1] 27419 14
raw_data %>% filter(is.na(inicio_visita) | is.na(fin_visita) )# A tibble: 52 × 14
id_orden cliente localidad direccion latitud longitud bultos unidades peso
<dbl> <fct> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
1 75370 20 CAPITAL FE… AVDA RIV… -34.6 -58.4 3 3 15
2 79712 20 CAPITAL FE… CORDOBA … -34.6 -58.4 2 2 11.9
3 79827 20 CAPITAL FE… MITRE 22… -34.6 -58.4 2 2 13
4 83443 20 CAPITAL FE… B MITRE … -34.6 -58.4 2 2 13
5 86129 20 CAPITAL FE… URIBURU … -34.6 -58.4 2 2 13
6 86163 20 CAPITAL FE… AV. CORR… -34.6 -58.4 2 2 13
7 91689 20 CAPITAL FE… PASTEUR … -34.6 -58.4 2 2 10.8
8 97879 20 CAPITAL FE… AV. CORR… -34.6 -58.4 2 2 13
9 99075 20 CAPITAL FE… PARAGUAY… -34.6 -58.4 2 2 11.9
10 99076 20 CAPITAL FE… AV. CORD… -34.6 -58.4 2 2 10.8
# ℹ 42 more rows
# ℹ 5 more variables: inicio_horario <chr>, fin_horario <chr>,
# visita_planificada <dttm>, inicio_visita <dttm>, fin_visita <dttm>
# Cantidad de valores con horarios iguales
dim(raw_data %>% filter(inicio_visita == fin_visita))[1] 10225 14
Como un gran porcentaje de los datos tiene el mismo inicio_visita y fin_visita almacenamos en variables distintas para intentar identificar un motivo.
# Guardamos en una variable los valores con horarios duplicados
datos_visita_duplicados <- raw_data %>%
filter(inicio_visita == fin_visita)
# Tambien una variable sin los duplicados
datos_visita_sin_duplicado <- raw_data %>%
filter(inicio_visita != fin_visita)# Ver los valores únicos y la cantidad de cada uno
raw_data %>%
count(fin_horario)# A tibble: 3 × 2
fin_horario n
<chr> <int>
1 1400 6
2 1401 27412
3 2359 1
Creamos nuevas columnas útiles para futuros analisis.
colnames(raw_data) [1] "id_orden" "cliente" "localidad"
[4] "direccion" "latitud" "longitud"
[7] "bultos" "unidades" "peso"
[10] "inicio_horario" "fin_horario" "visita_planificada"
[13] "inicio_visita" "fin_visita"
raw_data <- raw_data %>%
mutate(
dia = as.integer(format(fin_visita, "%d")), # Extraer el día
mes = as.integer(format(fin_visita, "%m")), # Extraer el mes
hora = as.integer(format(fin_visita, "%H")),
diferencia_minutos = as.numeric(
difftime(fin_visita, visita_planificada, units = "mins")),
dia_str = weekdays(fin_visita, abbreviate = FALSE),
duracion_visita_min = as.numeric(
difftime(fin_visita, inicio_visita, units = "mins")),
duracion_visita_horas = as.numeric(
difftime(fin_visita, inicio_visita, units = "hours"))
)# Tabla de valores faltantes por columna
inspect_na(raw_data) %>%
show_plot()
# Visualizar los valores faltantes
plot_missing(raw_data)
skim(raw_data)| Name | raw_data |
| Number of rows | 27419 |
| Number of columns | 21 |
| _______________________ | |
| Column type frequency: | |
| character | 5 |
| factor | 1 |
| numeric | 12 |
| POSIXct | 3 |
| ________________________ | |
| Group variables | None |
Variable type: character
| skim_variable | n_missing | complete_rate | min | max | empty | n_unique | whitespace |
|---|---|---|---|---|---|---|---|
| localidad | 0 | 1 | 5 | 31 | 0 | 43 | 0 |
| direccion | 0 | 1 | 7 | 49 | 0 | 6060 | 0 |
| inicio_horario | 0 | 1 | 1 | 3 | 0 | 3 | 0 |
| fin_horario | 0 | 1 | 4 | 4 | 0 | 3 | 0 |
| dia_str | 52 | 1 | 5 | 9 | 0 | 7 | 0 |
Variable type: factor
| skim_variable | n_missing | complete_rate | ordered | n_unique | top_counts |
|---|---|---|---|---|---|
| cliente | 0 | 1 | FALSE | 2 | 20: 16545, 70: 10874 |
Variable type: numeric
| skim_variable | n_missing | complete_rate | mean | sd | p0 | p25 | p50 | p75 | p100 | hist |
|---|---|---|---|---|---|---|---|---|---|---|
| id_orden | 0 | 1 | 101739.25 | 33854.63 | 74956.00 | 77464.50 | 82362.00 | 121907.00 | 183277.00 | ▇▁▁▁▂ |
| latitud | 0 | 1 | -34.60 | 0.04 | -34.93 | -34.62 | -34.60 | -34.58 | -32.94 | ▇▁▁▁▁ |
| longitud | 0 | 1 | -58.44 | 0.12 | -68.74 | -58.48 | -58.44 | -58.40 | -57.94 | ▁▁▁▁▇ |
| bultos | 0 | 1 | 5.76 | 12.25 | 0.10 | 2.00 | 3.00 | 6.00 | 360.00 | ▇▁▁▁▁ |
| unidades | 0 | 1 | 28.40 | 61.81 | 1.00 | 2.00 | 6.00 | 40.00 | 2203.00 | ▇▁▁▁▁ |
| peso | 0 | 1 | 41.16 | 79.15 | 0.00 | 13.00 | 20.94 | 39.00 | 2475.00 | ▇▁▁▁▁ |
| dia | 52 | 1 | 15.83 | 8.82 | 1.00 | 8.00 | 15.00 | 24.00 | 31.00 | ▇▇▇▆▆ |
| mes | 52 | 1 | 6.06 | 0.85 | 5.00 | 5.00 | 6.00 | 7.00 | 8.00 | ▇▇▁▇▁ |
| hora | 52 | 1 | 14.12 | 2.02 | 7.00 | 13.00 | 14.00 | 16.00 | 23.00 | ▁▅▇▂▁ |
| diferencia_minutos | 52 | 1 | 319.79 | 238.73 | -6820.00 | 217.91 | 321.98 | 403.00 | 7341.00 | ▁▁▇▁▁ |
| duracion_visita_min | 52 | 1 | 5.98 | 10.55 | 0.00 | 0.00 | 2.98 | 8.67 | 391.07 | ▇▁▁▁▁ |
| duracion_visita_horas | 52 | 1 | 0.10 | 0.18 | 0.00 | 0.00 | 0.05 | 0.14 | 6.52 | ▇▁▁▁▁ |
Variable type: POSIXct
| skim_variable | n_missing | complete_rate | min | max | median | n_unique |
|---|---|---|---|---|---|---|
| visita_planificada | 0 | 1 | 2024-05-03 12:01:00 | 2024-08-06 12:01:00 | 2024-06-18 12:03:00 | 6795 |
| inicio_visita | 52 | 1 | 2024-05-03 10:17:51 | 2024-08-06 19:57:00 | 2024-06-18 14:01:44 | 16350 |
| fin_visita | 52 | 1 | 2024-05-03 11:08:53 | 2024-08-06 19:57:00 | 2024-06-18 14:19:11 | 16302 |
Descripcion aca
plot_intro(raw_data)
raw_data %>%
inspect_cat() %>% # library(inspectdf)
show_plot()
Para entender la distribución de las variables numéricas como diferencia_minutos, duracion_visita_min, duracion_visita_horas, puedes visualizar los histogramas:
# Visualización de la distribución de variables numéricas
plot_histogram(raw_data)
# Seleccionar solo las variables numéricas
numericas <- raw_data %>%
select_if(is.numeric)
# Calcular la matriz de correlación
correlacion <- cor(numericas, use = "complete.obs")
# Visualizar la matriz de correlación con plotly
plot_ly(z = correlacion,
x = colnames(correlacion),
y = rownames(correlacion),
type = "heatmap",
colorscale = "Viridis") %>%
layout(title = "Matriz de Correlación entre Variables Numéricas",
xaxis = list(title = ""),
yaxis = list(title = ""))Warning: Ignoring 1 observations
# Visualización de la distribución de visitas por cliente
distribucion_cliente <- raw_data %>%
count(cliente)
plot_ly(distribucion_cliente,
x = ~cliente,
y = ~n,
type = 'bar',
marker = list(color = 'rgba(26, 118, 255, 0.6)',
line = list(color = 'rgba(26, 118, 255, 1.0)', width = 2))) %>%
layout(title = "Distribución de Visitas por Cliente",
xaxis = list(title = "Cliente"),
yaxis = list(title = "Cantidad de Visitas"),
showlegend = FALSE)# Relación entre 'dia_str' (día de la semana) y 'duracion_visita_min'
ggplot(raw_data, aes(x = dia_str, y = duracion_visita_min)) +
geom_boxplot() +
theme_minimal() +
labs(title = "Duración de la visita por día de la semana", x = "Día", y = "Duración de la visita (minutos)")Warning: Removed 52 rows containing non-finite outside the scale range
(`stat_boxplot()`).

# Contar visitas por día de la semana
raw_data %>%
count(dia_str) %>%
ggplot(aes(x = dia_str, y = n)) +
geom_bar(stat = "identity") +
labs(title = "Cantidad de visitas por día de la semana", x = "Día", y = "Número de visitas") +
theme_minimal()
raw_data$visita_planificada <- as.Date(raw_data$visita_planificada)
# Contar la cantidad de entregas por cliente y fecha
entregas_por_cliente <- raw_data %>%
group_by(visita_planificada, cliente) %>%
summarise(cantidad_entregas = n()) %>%
ungroup()`summarise()` has grouped output by 'visita_planificada'. You can override
using the `.groups` argument.
# Graficar con plotly
plot_ly(entregas_por_cliente,
x = ~visita_planificada,
y = ~cantidad_entregas,
color = ~cliente,
type = 'scatter',
mode = 'lines+markers') %>%
layout(title = "Cantidad de Entregas por Fecha para Cada Cliente",
xaxis = list(title = "Fecha de Visita Planificada"),
yaxis = list(title = "Cantidad de Entregas"),
legend = list(title = list(text = "Cliente")))Warning in RColorBrewer::brewer.pal(N, "Set2"): minimal value for n is 3, returning requested palette with 3 different levels
Warning in RColorBrewer::brewer.pal(N, "Set2"): minimal value for n is 3, returning requested palette with 3 different levels
Notamos que la caida del 7 de julio representa una entrega un día domingo. Día donde tipicamente no se realizan entregas. Tambien es una entrega con mucha demora.
# Asegúrate de que la columna que contiene la fecha esté en formato de fecha
raw_data$visita_planificada <- as.Date(raw_data$visita_planificada)
# Calcular el tiempo de demora en horas
raw_data <- raw_data %>%
mutate(demora_horas = as.numeric(difftime(fin_visita, visita_planificada, units = "min")))
# Calcular el tiempo de demora promedio por cliente y fecha
demora_por_cliente <- raw_data %>%
group_by(visita_planificada, cliente) %>%
summarise(demora_promedio = mean(demora_horas, na.rm = TRUE)) %>%
ungroup()`summarise()` has grouped output by 'visita_planificada'. You can override
using the `.groups` argument.
# Graficar con plotly
plot_ly(demora_por_cliente,
x = ~visita_planificada,
y = ~demora_promedio,
color = ~cliente,
type = 'scatter',
mode = 'lines+markers') %>%
layout(title = "Tiempo de Demora Promedio (en horas) por Fecha y Cliente",
xaxis = list(title = "Fecha de Visita Planificada"),
yaxis = list(title = "Demora Promedio (horas)"),
legend = list(title = list(text = "Cliente")))Warning in RColorBrewer::brewer.pal(N, "Set2"): minimal value for n is 3, returning requested palette with 3 different levels
Warning in RColorBrewer::brewer.pal(N, "Set2"): minimal value for n is 3, returning requested palette with 3 different levels
# Graficar las entregas por cliente con colores distintos
plot_ly(
raw_data,
lat = ~latitud,
lon = ~longitud,
type = 'scattermapbox',
mode = 'markers',
color = ~cliente, # Asigna un color distinto por cliente
marker = list(size = 7, opacity = 0.3), # Ajusta el tamaño y la transparencia de los marcadores
text = ~paste("Cliente:", cliente, "<br>Dirección:", direccion) # Información al pasar el mouse
) %>%
layout(
mapbox = list(
accesstoken = mapbox_token,
center = list(lat = -34.6037, lon = -58.3816), # Coordenadas de Buenos Aires
zoom = 10, # Nivel de zoom
style = "open-street-map" # Estilo del mapa
),
title = "Mapa de Entregas en Buenos Aires por Cliente",
margin = list(r = 0, t = 0, b = 0, l = 0)
)Warning in RColorBrewer::brewer.pal(N, "Set2"): minimal value for n is 3, returning requested palette with 3 different levels
Warning in RColorBrewer::brewer.pal(N, "Set2"): minimal value for n is 3, returning requested palette with 3 different levels
# Calcular el tiempo de demora en horas
copy_data <- raw_data %>%
mutate(demora_horas = as.numeric(difftime(fin_visita, visita_planificada, units = "hours")))
# Calcular la cantidad de entregas por día por cliente
entregas_por_cliente <- copy_data %>%
group_by(visita_planificada, cliente) %>%
summarise(cantidad_entregas = n(),
demora_promedio = mean(demora_horas, na.rm = TRUE)) %>%
ungroup()`summarise()` has grouped output by 'visita_planificada'. You can override
using the `.groups` argument.
# Graficar usando plotly con dos ejes Y
plot_ly() %>%
# Línea de cantidad de entregas por día por cliente
add_lines(data = entregas_por_cliente,
x = ~visita_planificada,
y = ~cantidad_entregas,
color = ~cliente,
name = "Cantidad de Entregas",
yaxis = "y1") %>%
# Línea de tiempo promedio de demora por día por cliente
add_lines(data = entregas_por_cliente,
x = ~visita_planificada,
y = ~demora_promedio,
color = ~cliente,
name = "Demora Promedio (Horas)",
line = list(dash = 'dash'), # Línea punteada para diferenciarlas
yaxis = "y2") %>%
# Configuración de los ejes
layout(
title = "Entregas por Día y Tiempo Promedio de Demora por Cliente",
xaxis = list(title = "Fecha"),
yaxis = list(title = "Cantidad de Entregas", side = "left", showgrid = FALSE),
yaxis2 = list(title = "Demora Promedio (Horas)", side = "right", overlaying = "y", showgrid = FALSE),
legend = list(x = 0.1, y = 0.9)
)Warning in RColorBrewer::brewer.pal(N, "Set2"): minimal value for n is 3, returning requested palette with 3 different levels
Warning in RColorBrewer::brewer.pal(N, "Set2"): minimal value for n is 3, returning requested palette with 3 different levels
# Entregas por semana
# Convertir la columna de fecha a formato Date (si no lo está)
raw_data$fin_visita <- as.Date(raw_data$visita_planificada)
# Crear una columna que agrupe por semana (añadiendo el lunes como inicio de semana)
raw_data$semana_visita <- cut(raw_data$visita_planificada, breaks = "week", start.on.monday = TRUE)
# Contar la cantidad de entregas por cliente y semana
entregas_por_cliente <- raw_data %>%
group_by(semana_visita, cliente) %>%
summarise(cantidad_entregas = n()) %>%
ungroup()`summarise()` has grouped output by 'semana_visita'. You can override using the
`.groups` argument.
# Graficar con plotly
plot_ly(entregas_por_cliente,
x = ~semana_visita,
y = ~cantidad_entregas,
color = ~cliente,
type = 'scatter',
mode = 'lines+markers') %>%
layout(title = "Cantidad de Entregas Semanales por Cliente",
xaxis = list(title = "Semana de Visita Planificada"),
yaxis = list(title = "Cantidad de Entregas"),
legend = list(title = list(text = "Cliente")))Warning in RColorBrewer::brewer.pal(N, "Set2"): minimal value for n is 3, returning requested palette with 3 different levels
Warning in RColorBrewer::brewer.pal(N, "Set2"): minimal value for n is 3, returning requested palette with 3 different levels
# Crear una lista de fechas únicas extraídas de la columna fin_visita
fechas_unicas <- unique(as.Date(raw_data$fin_visita))
# Graficar las entregas por cliente con un slider para cambiar por fecha
plot_ly(
raw_data,
lat = ~latitud,
lon = ~longitud,
type = 'scattermapbox',
mode = 'markers',
color = ~cliente, # Asigna un color distinto por cliente
frame = ~as.Date(fin_visita), # Agregar la fecha como frame para la animación
marker = list(size = 7, opacity = 0.7), # Ajusta el tamaño y la transparencia de los marcadores
text = ~paste("Cliente:", cliente, "<br>Dirección:", direccion) # Información al pasar el mouse
) %>%
layout(
mapbox = list(
accesstoken = mapbox_token,
center = list(lat = -34.6037, lon = -58.3816), # Coordenadas de Buenos Aires
zoom = 10, # Nivel de zoom
style = "open-street-map" # Estilo del mapa
),
title = "Mapa de Entregas en Buenos Aires por Cliente",
margin = list(r = 0, t = 0, b = 0, l = 0)
) %>%
animation_opts(
frame = 500, # Duración de cada frame en milisegundos
transition = 0, # Sin transiciones entre frames
redraw = TRUE
) %>%
animation_slider(
currentvalue = list(prefix = "Fecha: ")
)Warning in RColorBrewer::brewer.pal(N, "Set2"): minimal value for n is 3, returning requested palette with 3 different levels
Warning in RColorBrewer::brewer.pal(N, "Set2"): minimal value for n is 3, returning requested palette with 3 different levels
# Mapa de calor de entregas
heatmap_data <- raw_data %>%
group_by(latitud, longitud) %>%
summarise(total_entregas = n())`summarise()` has grouped output by 'latitud'. You can override using the
`.groups` argument.
# Graficar un mapa de calor para visualizar las zonas con mayor densidad de entregas
heatmap_plot <- plot_ly(
heatmap_data,
lat = ~latitud,
lon = ~longitud,
z = ~total_entregas,
type = 'densitymapbox',
colorscale = 'Viridis',
radius = 10
) %>%
layout(
mapbox = list(
accesstoken = mapbox_token,
center = list(lat = -34.6037, lon = -58.3816),
zoom = 10,
style = "open-street-map"
),
title = "Mapa de Calor de Entregas en Buenos Aires",
margin = list(r = 0, t = 30, b = 0, l = 0)
)
# Mostrar el mapa de calor
heatmap_plot# Mejorar el mapa original con clustering de puntos
clustered_plot <- plot_ly(
raw_data,
lat = ~latitud,
lon = ~longitud,
type = 'scattermapbox',
mode = 'markers',
color = ~as.factor(cliente),
marker = list(size = 7, opacity = 0.6),
text = ~paste("Cliente:", cliente, "<br>Dirección:", direccion)
) %>%
layout(
mapbox = list(
accesstoken = mapbox_token,
center = list(lat = -34.6037, lon = -58.3816),
zoom = 10,
style = "open-street-map"
),
title = "Mapa de Entregas en Buenos Aires por Cliente (con Clustering)",
margin = list(r = 0, t = 30, b = 0, l = 0)
)
# Mostrar el mapa mejorado con clustering
clustered_plotWarning in RColorBrewer::brewer.pal(N, "Set2"): minimal value for n is 3, returning requested palette with 3 different levels
Warning in RColorBrewer::brewer.pal(N, "Set2"): minimal value for n is 3, returning requested palette with 3 different levels
# 1. Gráfico de barras: Distribución de entregas por día de la semana
dia_entregas <- raw_data %>%
group_by(dia_str) %>%
summarise(total_entregas = n())
dia_entregas_plot <- ggplot(dia_entregas, aes(x = reorder(dia_str, total_entregas), y = total_entregas, fill = dia_str)) +
geom_bar(stat = "identity") +
labs(title = "Distribución de Entregas por Día de la Semana", x = "Día de la Semana", y = "Número de Entregas") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
# Mostrar el gráfico de barras
dia_entregas_plot
# 2. Gráfico de líneas: Entregas a lo largo del tiempo por mes
entregas_por_mes <- raw_data %>%
group_by(mes) %>%
summarise(total_entregas = n())
entregas_mes_plot <- ggplot(entregas_por_mes, aes(x = mes, y = total_entregas, group = 1)) +
geom_line(color = "blue", size = 1) +
geom_point(size = 2) +
labs(title = "Entregas a lo Largo del Tiempo por Mes", x = "Mes", y = "Número de Entregas") +
theme_minimal()Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
ℹ Please use `linewidth` instead.
# Mostrar el gráfico de líneas
entregas_mes_plotWarning: Removed 1 row containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 1 row containing missing values or values outside the scale range
(`geom_point()`).

# 3. Gráfico de cajas: Duración de visitas por cliente
duracion_visitas_plot <- ggplot(raw_data, aes(x = as.factor(cliente), y = duracion_visita_min, fill = as.factor(cliente))) +
geom_boxplot() +
labs(title = "Duración de Visitas por Cliente", x = "Cliente", y = "Duración de la Visita (minutos)") +
theme_minimal()
# Mostrar el gráfico de cajas
duracion_visitas_plotWarning: Removed 52 rows containing non-finite outside the scale range
(`stat_boxplot()`).

# 4. Gráfico de barras: Entregas por hora del día
entregas_por_hora <- raw_data %>%
group_by(hora) %>%
summarise(total_entregas = n())
entregas_hora_plot <- ggplot(entregas_por_hora, aes(x = hora, y = total_entregas, fill = as.factor(hora))) +
geom_bar(stat = "identity") +
labs(title = "Entregas por Hora del Día", x = "Hora del Día", y = "Número de Entregas") +
theme_minimal()
# Mostrar el gráfico de barras de entregas por hora
dia_entregas_plot
# 5. Análisis de tiempos de inactividad
# Calcular el tiempo de inactividad entre entregas por cliente
raw_data <- raw_data %>%
arrange(cliente, inicio_visita) %>%
group_by(cliente) %>%
mutate(tiempo_inactividad = as.numeric(difftime(inicio_visita, lag(fin_visita), units = "mins")))
# Limitar los valores extremos para mejorar la visualización
tiempo_inactividad_limited <- raw_data %>%
filter(tiempo_inactividad < 1500 & tiempo_inactividad >= 0)
# Gráfico violin: Tiempos de inactividad por cliente
tiempo_inactividad_violin_plot <- ggplot(tiempo_inactividad_limited, aes(x = as.factor(cliente), y = tiempo_inactividad, fill = as.factor(cliente))) +
geom_violin(trim = FALSE) +
geom_jitter(width = 0.2, alpha = 0.4) +
labs(title = "Tiempos de Inactividad por Cliente (Mejorado)", x = "Cliente", y = "Tiempo de Inactividad (minutos)") +
theme_minimal()
# Mostrar el gráfico de violín de tiempos de inactividad
tiempo_inactividad_violin_plot
# 6. Análisis para el Cliente 20
cliente_20_data <- raw_data %>% filter(cliente == 20)
# Gráfico de barras: Entregas por día de la semana para Cliente 20
cliente_20_dia_entregas <- cliente_20_data %>%
group_by(dia_str) %>%
summarise(total_entregas = n())
cliente_20_dia_entregas_plot <- ggplot(cliente_20_dia_entregas, aes(x = reorder(dia_str, total_entregas), y = total_entregas, fill = dia_str)) +
geom_bar(stat = "identity") +
labs(title = "Distribución de Entregas por Día de la Semana para Cliente 20", x = "Día de la Semana", y = "Número de Entregas") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
# Mostrar el gráfico de barras para Cliente 20
cliente_20_dia_entregas_plot
# 7. Análisis para el Cliente 70
cliente_70_data <- raw_data %>% filter(cliente == 70)
# Gráfico de barras: Entregas por día de la semana para Cliente 70
cliente_70_dia_entregas <- cliente_70_data %>%
group_by(dia_str) %>%
summarise(total_entregas = n())
cliente_70_dia_entregas_plot <- ggplot(cliente_70_dia_entregas, aes(x = reorder(dia_str, total_entregas), y = total_entregas, fill = dia_str)) +
geom_bar(stat = "identity") +
labs(title = "Distribución de Entregas por Día de la Semana para Cliente 70", x = "Día de la Semana", y = "Número de Entregas") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
# Mostrar el gráfico de barras para Cliente 70
cliente_70_dia_entregas_plot 
# Gráfico de líneas: Entregas por hora del día para Cliente 20 y Cliente 70
cliente_20_hora_entregas <- cliente_20_data %>%
group_by(hora) %>%
summarise(total_entregas = n())
cliente_70_hora_entregas <- cliente_70_data %>%
group_by(hora) %>%
summarise(total_entregas = n())
cliente_20_hora_entregas_plot <- ggplot(cliente_20_hora_entregas, aes(x = hora, y = total_entregas, fill = as.factor(hora))) +
geom_bar(stat = "identity") +
labs(title = "Entregas por Hora del Día para Cliente 20", x = "Hora del Día", y = "Número de Entregas") +
theme_minimal()
cliente_70_hora_entregas_plot <- ggplot(cliente_70_hora_entregas, aes(x = hora, y = total_entregas, fill = as.factor(hora))) +
geom_bar(stat = "identity") +
labs(title = "Entregas por Hora del Día para Cliente 70", x = "Hora del Día", y = "Número de Entregas") +
theme_minimal()
# Mostrar gráficos de barras por hora del día para Cliente 20 y Cliente 70
cliente_20_hora_entregas_plotWarning: Removed 1 row containing missing values or values outside the scale range
(`geom_bar()`).

cliente_70_hora_entregas_plot
# Gráfico de líneas combinado: Entregas por hora del día para Cliente 20 y Cliente 70
cliente_20_hora_entregas <- cliente_20_data %>%
group_by(hora) %>%
summarise(total_entregas = n()) %>%
mutate(cliente = "Cliente 20")
cliente_70_hora_entregas <- cliente_70_data %>%
group_by(hora) %>%
summarise(total_entregas = n()) %>%
mutate(cliente = "Cliente 70")
# Unir los datos de ambos clientes para el gráfico combinado
total_entregas_por_hora <- bind_rows(cliente_20_hora_entregas, cliente_70_hora_entregas)
# Crear el gráfico combinado
entregas_hora_combined_plot <- ggplot(total_entregas_por_hora, aes(x = hora, y = total_entregas, fill = cliente)) +
geom_bar(stat = "identity", position = "dodge") +
labs(title = "Entregas por Hora del Día para Clientes 20 y 70", x = "Hora del Día", y = "Número de Entregas", fill = "Cliente") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
# Mostrar gráfico combinado
entregas_hora_combined_plotWarning: Removed 1 row containing missing values or values outside the scale range
(`geom_bar()`).
